package com.flick.dao.impl;

import com.flick.common.*;
import com.flick.dao.MongoDbDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Repository
public abstract class MongoDbDaoImpl<T> implements MongoDbDao<T> {

	@Autowired
	private MongoTemplate mongoTemplate;
	private Class<T> entityClass;

	/**
	 * 这个通常也是hibernate的取得子类class的方法
	 *
	 * @author flick
	 * @date 2017-4-24
	 */
	public MongoDbDaoImpl() {
		Type genType = getClass().getGenericSuperclass();
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		entityClass = (Class) params[0];
	}
	
	public void insert(T t) {
		String collectionName = StrUtil.firstToLower(t.getClass().getSimpleName());
		mongoTemplate.insert(t, collectionName);
	}

	public T get(Object id) {
		String collectionName = StrUtil.firstToLower(entityClass.getSimpleName());
		return mongoTemplate.findOne(new Query(Criteria.where("id").is(id)), entityClass,collectionName);
	}

	public List<T> list(Map<String, T> params) {
		String collectionName = StrUtil.firstToLower(entityClass.getSimpleName());
		return mongoTemplate.findAll(entityClass, collectionName);
	}
	
	public Page page(Integer pageNum, Integer pageSize, String sidx, String sord, Filters... filters) {
		Query query = new Query();
		Criteria criteria = new Criteria();
		if(filters !=null){
			List<Criteria> criteriaListAnd = new ArrayList<Criteria>();
			List<Criteria> criteriaListOr = new ArrayList<Criteria>();
			for (Filters filter : filters) {
				if(filter!=null){
					String op = filter.getGroupOp();
					List<Rule> rules = filter.getRules();
					if(rules!=null && rules.size()>0){
						for (int i=0;i<rules.size();i++) {
							Rule rule = rules.get(i);
							if("AND".equals(op.toUpperCase())){
								criteriaListAnd.add(opToMongo(rule));
							}else if("OR".equals(op.toUpperCase())){
								criteriaListOr.add(opToMongo(rule));
							}
						}
					}
				}
			}
			if(criteriaListAnd.size()>0){
				Criteria[] criterAnds = new Criteria[criteriaListAnd.size()];
				criteria.andOperator(criteriaListAnd.toArray(criterAnds));
			}
			if(criteriaListOr.size()>0)
				criteria.orOperator((Criteria[]) criteriaListOr.toArray());
			query.addCriteria(criteria);
		}
		query.skip(pageSize*(pageNum-1)).limit(pageSize);
		String collectionName = StrUtil.firstToLower(entityClass.getSimpleName());
		Long count = mongoTemplate.count(query,entityClass, collectionName);
		if(sidx!=null){
			if(Common.notNull(sord)&&"desc".equals(sord.toLowerCase()))
				query.with(new Sort(Direction.DESC,sidx));
			else
				query.with(new Sort(Direction.ASC,sidx));
		}
		List<T> list = mongoTemplate.find(query,entityClass, collectionName);
		Integer maxPage = (int)(Math.ceil(((double)count)/pageSize));
		Page page = new Page();
		page.setCount(count);
		page.setMaxPage(maxPage);
		page.setObjList(list);
		page.setPageNum(pageNum);
		page.setPageSize(pageSize);
		return page;
	}

	public void update(Map<String, T> params) {
		// TODO Auto-generated method stub
		
	}

	public void createCollection(String collectionName) {
		mongoTemplate.createCollection(collectionName);
		
	}

	public void del(Object id) {
		String collectionName = StrUtil.firstToLower(entityClass.getSimpleName());
		mongoTemplate.remove(new Query(Criteria.where("id").is(id)),entityClass,collectionName);
	}
	
	
	/**
	 * 通过前端操作转换为sql操作符
	 * @param rule
	 * @return
	 */
	public Criteria opToMongo(Rule rule){
		String op = rule.getOp();
		String filed = rule.getField();
		String data = rule.getData();
		Criteria criteria = new Criteria();
		if(op != null) {
			switch (op) {
				case "eq":
					//			sqlop = "=";
					criteria = Criteria.where(filed).is(escapeExprSpecialWord(data));
					break;
				case "ne":
					criteria = Criteria.where(filed).ne(data);
					//			sqlop = "<>";
					break;
				case "lt":
					criteria = Criteria.where(filed).lt(data);
					//			sqlop = "<";
					break;
				case "le":
					criteria = Criteria.where(filed).lte(data);
					//			sqlop = "<=";
					break;
				case "gt":
					criteria = Criteria.where(filed).gt(data);
					//			sqlop = ">";
					break;
				case "ge":
					criteria = Criteria.where(filed).gte(data);
					//			sqlop = ">=";
					break;
				case "bw":
					criteria = Criteria.where(filed).gte(data);
					//			sqlop = ">=";
					break;
				case "bn":
					criteria = Criteria.where(filed).lt(data);
					//			sqlop = "<";
					break;
				case "in":
					//			sqlop = "in";
					criteria = Criteria.where(filed).in(data);
					break;
				case "ni":
					criteria = Criteria.where(filed).nin(data);
					//			sqlop = "not in";
					break;
				case "ew":
					criteria = Criteria.where(filed).lte(data);
					//			sqlop = "<=";
					break;
				case "en":
					criteria = Criteria.where(filed).gt(data);
					//			sqlop = ">";
					break;
				case "cn":
					criteria = Criteria.where(filed).regex(data);
					//			sqlop = "like";
					break;
				case "nc":
					criteria = Criteria.where(filed).regex(data).not();
					//			sqlop = "not like";
					break;
				default:
					criteria = Criteria.where(filed).is(escapeExprSpecialWord(data));
					//			sqlop = "=";
					break;
			}
		}
		return criteria;
	}
	
	/** 
	 * 转义正则特殊字符 （$()*+.[]?\^{},|） 
	 *  
	 * @param keyword 
	 * @return 
	 */  
	public String escapeExprSpecialWord(String keyword) {  
		String[] fbsArr = { "\\", "$","'","%", "*", "+", "[", "]", "?", "^", "{", "}", "|" };  
		for (String key : fbsArr) {
			if (keyword.contains(key)) {
				keyword = keyword.replace(key, "\\\\" + key);
			}
		}  
	    return keyword;  
	}  

}
